#ifndef GAMEENGINE_GRAPHICS_SHADER_OBJECT_H
#define GAMEENGINE_GRAPHICS_SHADER_OBJECT_H

#include <GameEngine/Graphics/graphics_settings.h>

#include <GameEngine/Graphics/Shaders/shader_defs.h>
#include <GameEngine/Graphics/graphic_resource.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

class GLSLShaderProgram;

//! Implements a shader object
/*!
 *  A shader object is a small piece of compiled code that can be used jointly with
 *  a shader program. This class allows to share common elements of code between
 *  shader programs.
 *
 *  Let's assume that we define a function that converts color from RGB color space
 *  to HSV color space, and that we need this function in shader programs SP1 and
 *  SP2. One way to do this would be to create two compiled source codes for the HSV
 *  to RGB conversion function and assign each one to a different shader program, but
 *  this is not very efficient nor practical. It is much better to share the conversion
 *  function using a shader object, as in the following example:
 *  @code
 *  GLSLShaderProgram& SP1 = ..., SP2 = ...;
 *  
 *  // Create shader object
 *  GLSLShaderObject hsv_to_rgb_object(source_code,shader_type);
 *  
 *  // Add shader object to the shader programs
 *  SP1.add_object(hsv_to_rgb_object);
 *  SP2.add_object(hsv_to_rgb_object);
 *  
 *  // Re-link the programs to actually change their behavior
 *  SP1.link();
 *  SP2.link();
 *  @endcode
 *
 *  The shader objects are handled as resources, which allows them to be quickly and safely
 *  copied / deleted.
 *
 @sa The GLSLShaderProgram class, that allows to manipulate shader programs.
 @note This class must always be manipulated AFTER a valid OpenGL context has been created. If
       no OpenGL context is available, any call to any function or constructor of this class
	   will result in an application crash.
 */
class _Graphics_decl GLSLShaderObject : public GraphicResource {
	GAMEENGINE_DECLARE_RTTI_PARENT(GLSLShaderObject,GraphicResource)
	//! The shader program class is friend of the shader object class to allow direct data access
	friend class GLSLShaderProgram;
	//! The resource manager class is friend of the shader object class because it must be able to access its ID and context
	friend class ResourceManager<ID,CONTEXTID>;

public:
	//! Type of the shader object
	enum Type {
		//! Vertex shader object
		Vertex = GL_VERTEX_SHADER,
		//! Fragment shader object
		Fragment = GL_FRAGMENT_SHADER,
		//! Geometry shader object
		Geometry = GL_GEOMETRY_SHADER_EXT
	};

public:
	GLSLShaderObject();
	GLSLShaderObject(const char* source, Type type, bool from_file=true);
	GLSLShaderObject(const GLSLShaderObject& rhs);
	virtual ~GLSLShaderObject();
	GLSLShaderObject& operator=(const GLSLShaderObject& rhs);

	bool operator==(const GLSLShaderObject& rhs) const;
	bool operator!=(const GLSLShaderObject& rhs) const;
	bool operator<(const GLSLShaderObject& rhs) const;

	bool set_shader(const char* source, Type type, bool from_file=true, bool compile_immediately=true);

	bool compile() const;

private:
	GEString load_file(const char* filename);
	bool check_compiling(const char* message) const;

	void clean_id();
	void create_id(Type type);

	virtual void destroy_resource() const;
	virtual CONTEXTID context() const;
	virtual ID reference() const;

private:
	//! Identifier of the shader object
	GLuint shader_object_;
};

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
